package jpa.generate.id;

import java.io.Serializable;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type;

public class SelfIdGenerator implements IdentifierGenerator {

  private long dataCenterId = 1;
  private long machineId = 1;
  private long twepoch = 0;
  private long sequence = 0;
  private long lastMills = 0;

  @Override
  public void configure(Type type, Properties params, ServiceRegistry serviceRegistry)
      throws MappingException {
    String dataCenterId1 = params.getProperty("dataCenterId");
    if (dataCenterId1 != null) {
      dataCenterId = Long.parseLong(dataCenterId1);
    }
    String machineId1 = params.getProperty("machineId");
    if (machineId1 != null) {
      machineId = Long.parseLong(machineId1);
    }

    String twepoch1 = params.getProperty("twepoch");
    if (twepoch1 != null) {
      twepoch = Long.parseLong(twepoch1);
    }
  }

  @Override
  public Serializable generate(SharedSessionContractImplementor session, Object object)
      throws HibernateException {
    long millis = System.currentTimeMillis();
    sequence = (sequence + 1) % 0x7f;
    if (sequence == 0) {
      //new mills segment
      sequence++;
      while (lastMills == millis) {
        millis = System.currentTimeMillis();
      }
    }
    lastMills = millis;
    long timestamp = millis - twepoch << 22;
    if(timestamp < 0){
      throw new IllegalArgumentException(String.format(
          "dataCenterId=%d, machineId=%d, time is consumed out, start millis at %d, now is %d",
          dataCenterId, machineId, twepoch, millis
      ));
    }
    return timestamp | dataCenterId << 17 | machineId << 12 | sequence;
  }
}
